LeakCanary 是 Square 公司所開發出來的檢測 Memory leak 的工具。不需要撰寫程式碼就可以自動偵測 Activity 與 Fragment 是否 Memory leak。
首先加入 dependencies
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
除了自動測偵的項目,你也可以使用 AppWatcher.objectWatcher.watch
加上想監控的物件。
AppWatcher.objectWatcher.watch(myView, "View was detached")
我們把之前 Memory leak 篇所提到的範例來測試看看。這個範例 Activity 被一個 Singleton 所參考了,在 Activity 離開時無法釋放資源造成 Memory leak。
class MainActivity2 : AppCompatActivity() {
private val binding by lazy { ActivityMain2Binding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
//Activity 被一個 Singleton 參考
Class1.context = this
}
}
object Class1 {
lateinit var context: Context
}
由於 LeakCanary 是自動偵測,所以直接執行 App 即可開始偵測。當偵測到 Memory leak,即會用通知的方式顯示。
點下通知,可以看到如下圖左邊畫面,是一個跟 Class1.context 有關的 Memory leak。再點進去可以看到下圖右邊畫面表示 Memory leak 發生在 MainActivity2,並會以紅色錯誤底線~~~來標示 Memory leak 的原因。
從 Logcat 也可以看到詳細的 Log。
====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS
evan.chen.toturial.memoryleaksample.MainActivity2 instance
Leaking: YES (ObjectWatcher was watching this because evan.chen.toturial.memoryleaksample.MainActivity2 received
Activity#onDestroy() callback and Activity#mDestroyed is true)
Retaining 90.0 kB in 1473 objects
key = c2b015e6-8494-485c-9a73-40e7c876681b
watchDurationMillis = 101708
retainedDurationMillis = 96703
mApplication instance of android.app.Application
mBase instance of androidx.appcompat.view.ContextThemeWrapper
再看另一個範例 Activity 未取消註冊 Broadcast 造成 Memory leak。
class MainActivity5 : AppCompatActivity() {
private val binding by lazy { ActivityMain5Binding.inflate(layoutInflater) }
private lateinit var broadcastReceiver: MyBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
broadcastReceiver = MyBroadcastReceiver()
}
override fun onResume() {
super.onResume()
val intentFilter = IntentFilter("evan.chen.tutorial.broadcastsample.Action1")
registerReceiver(broadcastReceiver, intentFilter)
}
override fun onPause() {
super.onPause()
//unregisterReceiver(broadcastReceiver)
}
}
執行後一樣會收到通知,點選通知可以看到 Memory leak 發生在 MainActivity5。
LeakCanary 用來測試是否有 Memory leak,可以一邊操作 App 一邊看是否有錯誤訊息。Profiler 則是必須 dump heap 才能發現是否有 Memory leak,所以兩個的功能還是有些差異。那麼 LeakCanary 是怎麼做到的呢?當 Activity 或 Fragment 被 destroy 後,ObjectWatcher 就會用 WeakReference 的方式參考著這些 Instance,在 GC 發生的幾秒內,如果這些參考沒有被移除,就被視為 Memory leak。
在記憶體的使用上,除了要避免 Memory leak ,還有其他要注意與分析的。在下一篇將接著介紹 Profiler 分析 App 的記憶體使用。